import axios, { AxiosRequestConfig, AxiosResponse, CancelTokenSource, InternalAxiosRequestConfig } from 'axios';
import globalConfig from './config';
import NProgress from './plugins/nprogress';
import { ResponseInfo } from './store/types';
import { getLocalStorageItem } from './utils/storage/localStorage';


// Customized options for the HTTP request
interface HttpRequestOptions {
  headers?: Record<string, string>;
  timeout?: number;
  cancelTokenSource?: CancelTokenSource;
}

// Encapsulated Axios
class HttpRequest {
  private baseUrl = import.meta.env.MOOD_BASE_API + '/api';
  private source: CancelTokenSource;

  // Constructor
  constructor() {
    axios.defaults.headers.post['Content-Type'] = 'application/json;charset=utf-8';
    axios.defaults.headers.patch['Content-Type'] = 'application/json;charset=utf-8';
    axios.defaults.baseURL = this.baseUrl;
    axios.defaults.timeout = 10000;
    this.source = axios.CancelToken.source();
  }

  // Request interceptor
  private interceptors(config: InternalAxiosRequestConfig<any>, options: HttpRequestOptions = {}) {
    NProgress.start();
    config.cancelToken = options.cancelTokenSource ? options.cancelTokenSource.token : this.source.token;
    const token = getLocalStorageItem<string>(globalConfig.userTokenLocal);

    if (token) {
      config.headers.Authorization = token;
    }

    if (options.headers) {
      config.headers = Object.assign(config.headers, options.headers);
    }

    return config;
  }

  // Response interceptor
  private interceptorsResponse(response: AxiosResponse<ResponseInfo<any>>) {
    NProgress.done();
    const { status, data, config } = response;
    if (status !== 200 || data.code === 0 || (data.code >= 400 && data.code < 500)) {
      return Promise.reject(data.message || 'Request error!');
    }

    return data.data;
  }

  // Make a request
  public request(config: AxiosRequestConfig, options: HttpRequestOptions = {}): Promise<any> {
    const instance = axios.create();
    instance.interceptors.request.use((requestConfig) => this.interceptors(requestConfig, options));
    instance.interceptors.response.use(this.interceptorsResponse.bind(this), (error) => {
      if (error && error.response) {
        // Handle error responses
      }
      return Promise.reject(error);
    });
    return instance(config) as Promise<any>;
  }

  // Cancel a request
  public cancel(message?: string): void {
    this.source.cancel(message);
  }

  // Make a GET request
  public get(url: string, data?: any, options: HttpRequestOptions = {}): Promise<any> {
    const config: AxiosRequestConfig = {
      method: 'get',
      url,
      params: data
    };
    return this.request(config, options);
  }

  // Make a POST request
  public post(url: string, data?: any, options: HttpRequestOptions = {}): Promise<any> {
    const config: AxiosRequestConfig = {
      method: 'post',
      url,
      data
    };
    return this.request(config, options);
  }

  // Make a PUT request
  public put(url: string, data?: any, options: HttpRequestOptions = {}): Promise<any> {
    const config: AxiosRequestConfig = {
      method: 'put',
      url,
      data
    };
    return this.request(config, options);
  }

  // Make a PATCH request
  public patch(url: string, data?: any, options: HttpRequestOptions = {}): Promise<any> {
    const config: AxiosRequestConfig = {
      method: 'patch',
      url,
      data
    };
    return this.request(config, options);
  }

  // Make a DELETE request
  public delete(url: string, data?: any, options: HttpRequestOptions = {}): Promise<any> {
    const config: AxiosRequestConfig = {
      method: 'delete',
      url,
      params: data
    };
    return this.request(config, options);
  }
}

export default new HttpRequest();
